package api;

import bean.Docs;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import util.FileUtils;
import util.RequestUtils;


import java.io.IOException;
import java.util.ArrayList;

/**
*@ClassName: HttpList
*@Description: 获取文件信息和docs路径
*@Params:
*@Return:
*@Author xxw
*@Date 2021/12/24
*/

public class HttpList {
    private static String targetUrl = "https://www.yuque.com/api/docs?"; // 获取目录文件信息集合地址
    private static String exportUrl = "https://www.yuque.com/api/docs/"; //  获取 docs 下载链接的地址
    private static String cookieTextUrl = "cookie.txt";
    private static String cookie = ""; // 请求 cookie
    private static CloseableHttpClient httpClient; // http 对象
    private static ArrayList<String> pendingList; // 存放未知错误无法导出的文件名
    private static ArrayList<String> unpublishedList; // 存放未发布导致无法导出的文件名
    private static String pendingPath = "./pending.txt"; // 未知错误写入路径
    private static String unpublishedPath = "./unpublished.txt"; // 未发布写入路径
    private static String csrf_token = "";
    private static String csrfTokenUrl = "csrf.txt";

    // 静态区 从cookie.txt读取cookie, 获取http对象
    static {
        HttpList.cookie = FileUtils.readFile(cookieTextUrl);
        HttpList.csrf_token  = FileUtils.readFile(csrfTokenUrl);
        try {
            HttpList.httpClient = RequestUtils.getHttpClient();
        } catch (Exception e) {
            System.out.println("httpGet--> httpclient 对象创建异常！");
        }
    }
    // 获取文档 id 和 title
    public ArrayList<Docs> getFileInfoList(String id) {
        System.out.println("开始获取文档 id + title ...");
        ArrayList<Docs> fileInfoList = new ArrayList<>();
        // 目标地址
        String url = targetUrl + "book_id=" + id + "&include_last_editor=true&include_share=true&include_user=true&only_order_by_id=false";
        HttpGet httpGet = new HttpGet(url);
        // 添加请求头
        httpGet.setHeader("Cookie", cookie);
        httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36");
        httpGet.setHeader("Connection", "keep-alive");
        httpGet.setHeader("Host", "www.yuque.com");
        httpGet.setHeader("refer", "https://www.yuque.com/g/yigedaxigua-fjdea/hbw5og/docs");
        httpGet.setHeader("Sec-Fetch-Mode", "cors");
        httpGet.setHeader("x-csrf-token", "4fV9tkdkfcf_zofXGfm-fzrj");
        // 发送请求
        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(httpGet);
        } catch (IOException e) {
            System.out.println("httpGet--> CloseableHttpResponse 出现异常！");
            try {
                response.close();
                httpClient.close();
            } catch (IOException e1) {
                System.out.println("httpGet--> CloseableHttpResponse 关闭出现异常！");
            }
        }
        // 获得响应实体
        HttpEntity entity = response.getEntity();
        String resString = "";
        try {
            resString = EntityUtils.toString(entity, "UTF-8");
        } catch (IOException e) {
            System.out.println("httpGet--> 返回数据转字符串出现问题！");
        }
        // 响应数据处理
        JSONArray jsonArray = JSON.parseObject(resString).getJSONArray("data");
        if (jsonArray.size() > 0) {
            for (int i = 0; i < jsonArray.size(); i++) {
                JSONObject o = jsonArray.getJSONObject(i);
                fileInfoList.add(new Docs(o.getInteger("id"), o.getString("title"), "")); // 处理好的让入集合
            }
        }
        System.out.println("文档 id + title 获取完毕！总数为: " + fileInfoList.size() + " 篇。");
        return getDownLoadUrl(fileInfoList);
    }

    // 获取 docs 下载链接
    // 语雀很奇葩的操作： 经常出现 pending, 语雀的解决办法是循环发送请求，直到返回正常数据停止
    public ArrayList<Docs> getDownLoadUrl(ArrayList<Docs > fileInfoList) {
        System.out.println("开始获取下载链接...");
        System.out.println("================================");
        pendingList = new ArrayList<>(); // 存放未知错误无法导出的文件名
        unpublishedList = new ArrayList<>(); // 存放没有发布导致无法导出的文件名
        // 遍历请求所有下载地址并保存
        for (int i = 0; i < fileInfoList.size(); i++) {
            String httpUrl = exportUrl + fileInfoList.get(i).getId() + "/export?type=markdown";
            // 直到返回正常数据或者未发布时才停止循环
            String res = "";
            // 如果循环了十次还是没有数据那就停止
            int n = 0;
            do {
                if (++n > 10) {
                    System.out.println(" [ " + fileInfoList.get(i).getFilename()+ " ]" + " --->  已循环10次，一直处于 pending，自动跳出! ");
                    fileInfoList.get(i).setDocsDowUrl("null");
                    pendingList.add(fileInfoList.get(i).getFilename()); // 存入集合
                    break;
                }
                res = httpPostMethod(httpUrl, fileInfoList.get(i).getFilename());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    System.out.println("延时执行出现异常！");
                }
                if (res.equals("null")){
                    fileInfoList.get(i).setDocsDowUrl("null");
                    unpublishedList.add(fileInfoList.get(i).getFilename()); // 存入集合
                } else if (!res.equals("pending")) {
                    fileInfoList.get(i).setDocsDowUrl(res);
                    System.out.println(res);
                }
            } while (res.equals("pending"));
        }
        System.out.println("================================");
        System.out.println("下载链接获取完毕！ ");
        // 文件写入
        listWrite2File(pendingList, pendingPath);
        listWrite2File(unpublishedList, unpublishedPath);
        System.out.println("未知问题 && 未发布博客名已写入文件！");
        return fileInfoList;
    }

    // 单独提出发送 post 请求函数获取下载url , 后期好循环调用
    public String httpPostMethod (String url, String fileName) {
        HttpPost httpPost = new HttpPost(url);
        // 插入 json 数据
        httpPost.setEntity(new StringEntity("{\"type\":\"word\",\"force\":\"0\"}", "UTF-8"));
        // 请求头定义
        httpPost.setHeader("Cookie", cookie);
        httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36");
        httpPost.setHeader("Connection", "keep-alive");
        httpPost.setHeader("Host", "www.yuque.com");
        httpPost.setHeader("refer", "https://www.yuque.com/");
        httpPost.setHeader("Sec-Fetch-Mode", "cors");
        httpPost.setHeader("x-csrf-token", csrf_token);
        httpPost.setHeader("Content-Type", "application/json");
        // 发送请求
        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(httpPost);
        } catch (IOException e) {
            e.printStackTrace();
            try {
                response.close();
                httpClient.close();
            } catch (IOException e1) {
                System.out.println("httpPost--> CloseableHttpResponse 关闭出现异常！");
            }
        }
        // 获取响应实体
        HttpEntity entity = response.getEntity();
        String resString = "";
        try {
            resString = EntityUtils.toString(entity, "UTF-8");
        } catch (IOException e) {
            System.out.println("httpPost--> 返回数据转字符串出现问题！");
        }
        // 判断是否存在 "请发布后再导出" 字符串，存在无法获取下载链接
        if (resString.contains("请发布后再导出")) {
            System.out.println(fileName+ " 未发布，无法获取下载链接！！！");
            return "null";
        }
        // state 作判断
        String state = JSON.parseObject(JSON.parseObject(resString).getString("data")).getString("state");
        if (state.equals("success")) {
            // 返回 url 链接
            return JSON.parseObject(JSON.parseObject(resString).getString("data")).getString("url");
        } else if (state.equals("pending")) {
            // 出现 pending 需要一直发送请求直到返回url
            return "pending";
        }
        return "pending";
    }

    // pendingList, unpublishedList 写入文件
    public void listWrite2File(ArrayList<String> list, String path) {
        String content = "";
        for (String s : list) {
            content += s + "\r\n";
        }
        FileUtils.fileLinesWrite(path, content, false);
    }
 }
